头文件

  对同一个对象、函数、类等的所有声明的类型都必须一致。所以,递交给编译器的源代码以及后来被连接的东西也必须一致。要达到在不同编译单位中声明的一致性,有一种不完美但却比较简单的方法,那就是将包含界面信息的头文件通过#include包含到可执行代码和/或数据定义的源程序文件里。

  #include机制是一种正文操作的概念,用于将源程序片段收集到一起,形成一个提供给编译的单位(文件)。指令

#include "to_be_included"

将用文件to_be_included的内容取代这个#include所在的那一行。该文件的内容应该是C++源代码,因为编译器接着要去读它。

  如果要包含标准库文件,那么就应该使用尖括号 <> 而不是引号。例如,

#include <iostream>            // 来自标准库包含目录
#include "myheader.h"          // 来自当前目录

不幸的是,在一个包含指令中,<>"" 内部的空格也是有意义的:

#include < iostream >          // 将无法找到<iostream>

当一个文件被包含到某处之后,每次都需要重新去编译它,这看起来是过分奢侈了。不过,在典型情况下被包含的文件里只有声明,没有需要编译器深入分析的代码。进一步说,许多现在C++实现都提供了对头文件的某种预编译形式,以尽可能减少反复编译同一个头文件所需要的工作。

  作为一种经验法则,头文件里可以包括

命名名字空间 namespace N { /* ... */ }
类型定义 struct Point { int x, y; }
模板声明 template<class T> class Z;
模板定义 template<class T> class V { /* ... */ };
函数声明 extern int strlen(const char*);
在线函数定义 inline char get(char* p) { return *p++; }
数据声明 extern int a;
常量定义 const float pi = 3.141593;
枚举 enum Light { red, yellow, green };
名字声明 class Matrix;
包含指令 #include <algorithm>
宏定义 #define VERSION12
条件编译指令 #ifdef __cplusplus
注释 /* check for end of file */

这种有关什么可以放入头文件的经验法则并不是语言所要求的。它只是使用#include机制来表示程序的物理结构的一种合理方式。在另一方面,头文件里绝不应该有:

常规的函数定义 char get(char* p) { return *p++; }
数据定义 int a;
聚集量(aggregate)定义 short tb1[] = { 1, 2, 3 };
无名名字空间 namespace { /* ... */ }
导出的(exported)模板定义 export template<class T> f(T t) { /* ... */ }

按照习惯,头文件采用后缀 .h,而包含函数或数据定义的文件用 .c 后缀。它们也因此被分别称为“.h文件”和“.c文件”。其他约定,如.C、.cxx、.cpp和.cc等也常常可以看到。你的编译器手册在这个方面可以有一些特殊东西。

  上面建议只在头文件放简单常量的定义,而不放聚集量的定义,其原因是,具体实现很难避免聚集量在几个编译单位中的重复出现。进一步说,那些简单情况都是最常见的东西,因此对于生成好的代码也更重要一些。

  对于#include的使用不要过于自作聪明。我的建议是,只用#include包含完整的声明和定义,而且只在全局作用域中,或在连接描述块里,或在转换老代码时在名字空间定义里这样做(9.2.2节)。与别处一样,在这里也要避免玩弄宏魔术。我最不愿意做的事情之一就是:追踪由某个名字引起的一个错误,该名字被宏替换成另一个完全不同的东西,而有关的宏定义又是出现在某个我从来也没听说过的,通过#include间接包含进来的头文件里。

🔚